#pragma once
#include <tuple>
#include "tuple_hash.hpp"
#include "list_cpp.hpp"
#include "htab_cpp.hpp"
#include "elemtag.hpp"
template<int NTypes>
struct type_tuple_helper {
  typedef type_tuple_helper<NTypes - 1> next;
  typedef decltype(std::tuple_cat(std::make_tuple(std::declval<elemtag>()), std::declval<typename next::type>())) type;
};

template<>
struct type_tuple_helper<0>{
  typedef std::tuple<> type;
};
template<int NTypes>
using type_tuple = typename type_tuple_helper<NTypes>::type;
struct multiplicated_pid {
  int pid[7];
  int nmult;
};
const int MULTIPLICATED = -2;
template<typename T>
struct param_mapper{
  esmd::htab<tie_key<T>, int> param_htab;
  esmd::list<T> param_list;
  // esmd::htab<tuple_key_for<type_tuple<nbodies>>, int> type_htab;
  // esmd::htab<tuple_key_for<type_tuple<nbodies>>, multiplicated_pid> type_htab_multi;
  param_mapper(const char *name) : param_htab(name), param_list(name) {
  }
  int map(T &t) {
    int map_id = param_htab.setdefault(t.tie(), param_htab.size());
    if (map_id >= param_list.size()) param_list.append(t);
    return map_id;
  }
  // int print_t(elemtag tag) {
  //   return printf("%s ", tag.str) == 1;
  // }
  // template <typename... Ts, int... Is>
  // void print_tuple(std::tuple<Ts...> tpl, utils::iseq<int, Is...> seq) {
  //   int a[] = {print_t(std::get<Is>(tpl))...};
  // }
  // template <typename... Ts>
  // void print_tuple(std::tuple<Ts...> tpl) {
  //   print_tuple(tpl, utils::make_iseq<int, sizeof...(Ts)>{});
  // }
  // int map_type(const type_tuple<NTypes> &type, T &param) {
  //   int map_id = map(param);
  //   if (type_htab.contains(type)) {
  //     // printf("find multiplicity for ");
  //     // print_tuple(type);
  //     // puts("");
  //     if (map_id != type_htab[type]) {
  //       auto &multipid = type_htab_multi[type];
  //       if (type_htab[type] != MULTIPLICATED) {
  //         multipid.pid[0] = type_htab[type];
  //         multipid.nmult = 1;
  //         type_htab[type] = MULTIPLICATED;
  //       }
  //       multipid.pid[multipid.nmult++] = map_id;
  //     }
  //     return MULTIPLICATED;
  //   } else {
  //     type_htab[type] = map_id;
  //     return map_id;
  //   }
  // }
  // int get(const type_tuple<NTypes> &type) {
  //   if (type_htab.contains(type)) return type_htab[type];
  //   else return -1;
  // }
  // const multiplicated_pid &get_multi(const type_tuple<NTypes> &type) {
  //   if (type_htab.contains(type)) return type_htab_multi[type];
  //   else return *(multiplicated_pid*)nullptr;
  // }
};
